package errors

import (
	"errors"
	"fmt"

	"google.golang.org/genproto/googleapis/rpc/code"
	spb "google.golang.org/genproto/googleapis/rpc/status"
)

func newf(c code.Code, format string, a ...interface{}) *Status {
	return &Status{s: &spb.Status{Code: int32(c), Message: fmt.Sprintf(format, a...)}}
}

// Cancelled The operation was cancelled, typically by the caller.
// HTTP Mapping: 499 Client Closed Request
func Cancelled(format string, a ...interface{}) *Status {
	return newf(code.Code_CANCELLED, format, a...)
}

// IsCancelled determines if err is an error which indicates a cancelled error.
// It supports wrapped errors.
func IsCancelled(err error) bool {
	if se := new(Status); errors.As(err, &se) {
		return se.Code() == code.Code_CANCELLED
	}
	return false
}

// Unknown error.
// HTTP Mapping: 500 Internal Server Error
func Unknown(format string, a ...interface{}) *Status {
	return newf(code.Code_UNKNOWN, format, a...)
}

// IsUnknown determines if err is an error which indicates a unknown error.
// It supports wrapped errors.
func IsUnknown(err error) bool {
	if se := new(Status); errors.As(err, &se) {
		return se.Code() == code.Code_UNKNOWN
	}
	return false
}

// InvalidArgument The client specified an invalid argument.
// HTTP Mapping: 400 Bad Request
func InvalidArgument(format string, a ...interface{}) *Status {
	return newf(code.Code_INVALID_ARGUMENT, format, a...)
}

// IsInvalidArgument determines if err is an error which indicates an invalid argument error.
// It supports wrapped errors.
func IsInvalidArgument(err error) bool {
	if se := new(Status); errors.As(err, &se) {
		return se.Code() == code.Code_INVALID_ARGUMENT
	}
	return false
}

// DeadlineExceeded The deadline expired before the operation could complete.
// HTTP Mapping: 504 Gateway Timeout
func DeadlineExceeded(format string, a ...interface{}) *Status {
	return newf(code.Code_DEADLINE_EXCEEDED, format, a...)
}

// IsDeadlineExceeded determines if err is an error which indicates a deadline exceeded error.
// It supports wrapped errors.
func IsDeadlineExceeded(err error) bool {
	if se := new(Status); errors.As(err, &se) {
		return se.Code() == code.Code_DEADLINE_EXCEEDED
	}
	return false
}

// NotFound Some requested entity (e.g., file or directory) was not found.
// HTTP Mapping: 404 Not Found
func NotFound(format string, a ...interface{}) *Status {
	return newf(code.Code_NOT_FOUND, format, a...)
}

// IsNotFound determines if err is an error which indicates a not found error.
// It supports wrapped errors.
func IsNotFound(err error) bool {
	if se := new(Status); errors.As(err, &se) {
		return se.Code() == code.Code_NOT_FOUND
	}
	return false
}

// AlreadyExists The entity that a client attempted to create (e.g., file or directory) already exists.
// HTTP Mapping: 409 Conflict
func AlreadyExists(format string, a ...interface{}) *Status {
	return newf(code.Code_ALREADY_EXISTS, format, a...)
}

// IsAlreadyExists determines if err is an error which indicates a already exsits error.
// It supports wrapped errors.
func IsAlreadyExists(err error) bool {
	if se := new(Status); errors.As(err, &se) {
		return se.Code() == code.Code_ALREADY_EXISTS
	}
	return false
}

// PermissionDenied The caller does not have permission to execute the specified operation.
// HTTP Mapping: 403 Forbidden
func PermissionDenied(format string, a ...interface{}) *Status {
	return newf(code.Code_PERMISSION_DENIED, format, a...)
}

// IsPermissionDenied determines if err is an error which indicates a permission denied error.
// It supports wrapped errors.
func IsPermissionDenied(err error) bool {
	if se := new(Status); errors.As(err, &se) {
		return se.Code() == code.Code_PERMISSION_DENIED
	}
	return false
}

// ResourceExhausted Some resource has been exhausted, perhaps a per-user quota, or
// perhaps the entire file system is out of space.
// HTTP Mapping: 429 Too Many Requests
func ResourceExhausted(format string, a ...interface{}) *Status {
	return newf(code.Code_RESOURCE_EXHAUSTED, format, a...)
}

// IsResourceExhausted determines if err is an error which indicates a resource exhausted error.
// It supports wrapped errors.
func IsResourceExhausted(err error) bool {
	if se := new(Status); errors.As(err, &se) {
		return se.Code() == code.Code_RESOURCE_EXHAUSTED
	}
	return false
}

// FailedPrecondition The operation was rejected because the system is not in a state
// required for the operation's execution.
// HTTP Mapping: 400 Bad Request
func FailedPrecondition(format string, a ...interface{}) *Status {
	return newf(code.Code_FAILED_PRECONDITION, format, a...)
}

// IsFailedPrecondition determines if err is an error which indicates a failed precondition error.
// It supports wrapped errors.
func IsFailedPrecondition(err error) bool {
	if se := new(Status); errors.As(err, &se) {
		return se.Code() == code.Code_FAILED_PRECONDITION
	}
	return false
}

// Aborted The operation was aborted, typically due to a concurrency issue such as
// a sequencer check failure or transaction abort.
// HTTP Mapping: 409 Conflict
func Aborted(format string, a ...interface{}) *Status {
	return newf(code.Code_ABORTED, format, a...)
}

// IsAborted determines if err is an error which indicates an aborted error.
// It supports wrapped errors.
func IsAborted(err error) bool {
	if se := new(Status); errors.As(err, &se) {
		return se.Code() == code.Code_ABORTED
	}
	return false
}

// OutOfRange The operation was attempted past the valid range.  E.g., seeking or
// reading past end-of-file.
// HTTP Mapping: 400 Bad Request
func OutOfRange(format string, a ...interface{}) *Status {
	return newf(code.Code_OUT_OF_RANGE, format, a...)
}

// IsOutOfRange determines if err is an error which indicates a out of range error.
// It supports wrapped errors.
func IsOutOfRange(err error) bool {
	if se := new(Status); errors.As(err, &se) {
		return se.Code() == code.Code_OUT_OF_RANGE
	}
	return false
}

// Unimplemented The operation is not implemented or is not supported/enabled in this service.
// HTTP Mapping: 501 Not Implemented
func Unimplemented(format string, a ...interface{}) *Status {
	return newf(code.Code_UNIMPLEMENTED, format, a...)
}

// IsUnimplemented determines if err is an error which indicates a unimplemented error.
// It supports wrapped errors.
func IsUnimplemented(err error) bool {
	if se := new(Status); errors.As(err, &se) {
		return se.Code() == code.Code_UNIMPLEMENTED
	}
	return false
}

// Internal This means that some invariants expected by the
// underlying system have been broken.  This error code is reserved
// for serious errors.
//
// HTTP Mapping: 500 Internal Server Error
func Internal(format string, a ...interface{}) *Status {
	return newf(code.Code_INTERNAL, format, a...)
}

// IsInternal determines if err is an error which indicates an internal server error.
// It supports wrapped errors.
func IsInternal(err error) bool {
	if se := new(Status); errors.As(err, &se) {
		return se.Code() == code.Code_INTERNAL
	}
	return false
}

// Unavailable The service is currently unavailable.
// HTTP Mapping: 503 Service Unavailable
func Unavailable(format string, a ...interface{}) *Status {
	return newf(code.Code_UNAVAILABLE, format, a...)
}

// IsUnavailable determines if err is an error which indicates a unavailable error.
// It supports wrapped errors.
func IsUnavailable(err error) bool {
	if se := new(Status); errors.As(err, &se) {
		return se.Code() == code.Code_UNAVAILABLE
	}
	return false
}

// DataLoss Unrecoverable data loss or corruption.
// HTTP Mapping: 500 Internal Server Error
func DataLoss(format string, a ...interface{}) *Status {
	return newf(code.Code_DATA_LOSS, format, a...)
}

// IsDataLoss determines if err is an error which indicates a data loss error.
// It supports wrapped errors.
func IsDataLoss(err error) bool {
	if se := new(Status); errors.As(err, &se) {
		return se.Code() == code.Code_DATA_LOSS
	}
	return false
}

// Unauthorized The request does not have valid authentication credentials for the operation.
// HTTP Mapping: 401 Unauthorized
func Unauthorized(format string, a ...interface{}) *Status {
	return newf(code.Code_UNAUTHENTICATED, format, a...)
}

// IsUnauthorized determines if err is an error which indicates a unauthorized error.
// It supports wrapped errors.
func IsUnauthorized(err error) bool {
	if se := new(Status); errors.As(err, &se) {
		return se.Code() == code.Code_UNAUTHENTICATED
	}
	return false
}
